package com.baidu.bas.core.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.ObjectUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

/**
 * @author liyuepeng
 * @date 2018-11-07.
 */
@Slf4j
public class ReflectUtil {
    /**
     * 为一个enum字段的string类型转义为enum类型
     * @param clazz
     * @param value
     * @return
     */
    public static Object toEnumValue(Class<?> clazz,String value){
        try {
            Method valueOf = clazz.getMethod("valueOf", String.class);
            return valueOf.invoke(null, value);
        } catch ( ReflectiveOperationException e) {
            log.info("{} reflect to enum [{}] failed.",value,clazz);
            return null;
        }
    }

    /**
     * 获取对象空属性
     *
     * @param src 源对象
     * @return 空属性
     */
    public static String[] getNullProperties(Object src) {
        BeanWrapper srcBean = new BeanWrapperImpl(src);
        PropertyDescriptor[] pds = srcBean.getPropertyDescriptors();
        Set<String> properties = new HashSet<>();
        for (PropertyDescriptor propertyDescriptor : pds) {
            String propertyName = propertyDescriptor.getName();
            if ("class".equals(propertyName) || "new".equals(propertyName)) {
                continue;
            }
            Object propertyValue = srcBean.getPropertyValue(propertyName);
            if (!ObjectUtils.isEmpty(propertyValue)) {
                properties.add(propertyName);
            }
        }
        return properties.toArray(new String[0]);
    }

    //递归查找类字段
    public static Field findClassField(Class<?> clazz, String fieldName) {
        Field field = null;
        while (clazz != null) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
                if (Object.class.equals(clazz = clazz.getSuperclass())) {
                    break;
                }
            }
        }
        return field;
    }

    public static Object findFieldChain(String fieldName, Object obj)
            throws IllegalAccessException {
        String[] fields = fieldName.split("\\.");
        for (String field : fields) {
            obj = findClassField(obj.getClass(), field).get(obj);
            if (null == obj) {
                return null;
            }
        }
        return obj;
    }

    public static List<Field> findAllField(Class<?> clazz){
        List<Field> fields = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            int mod = field.getModifiers();
            if (Modifier.isStatic(mod) || Modifier.isInterface(mod)) {
                continue;
            }
            fields.add(field);
        }
        return fields;
    }

    public static void findClassAllFields(Class<?> clazz, Consumer<Field> fieldConsumer) {
        Class<?> tempClass = clazz;
        while (null != tempClass) {
            for (Field field : tempClass.getDeclaredFields()) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isInterface(mod)) {
                    continue;
                }
                fieldConsumer.accept(field);
            }
            tempClass = tempClass.getSuperclass();
        }
    }

    //获取字段泛型名
    public static List<String> getFieldGenericName(Field field) {
        List<String> names = new ArrayList<>();
        Type gType = field.getGenericType();
        if (gType instanceof ParameterizedType) {
            Type[] typeArguments = ((ParameterizedType) gType).getActualTypeArguments();
            for (Type typeArgument : typeArguments) {
                String[] gArray = typeArgument.getTypeName().split("\\.");
                names.add(gArray[gArray.length - 1]);
            }
        }
        return names;
    }
}
